﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Configuration;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using DataGenerator.DefaultSets;
using Dispatcher.Extensions;
using Dispatcher.Helpers;
using Dispatcher.Properties;
using Dispatcher.Views;
using Foundation;
using Repository;

namespace Dispatcher.ViewModels
{
    /// <summary>
    /// Event Edit View Model.
    /// </summary>
    public class EventEditViewModel : ViewModel
    {
        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="EventEditViewModel"/> class.
        /// </summary>
        /// <param name="modelContainer">The model container.</param>
        /// <param name="start">The start.</param>
        /// <param name="end">The end.</param>
        public EventEditViewModel(ModelContainer modelContainer, TimeSpan start, TimeSpan end)
        {
            ModelContainer = modelContainer;
            CommandBindings.Add(new CommandBinding(ApplicationCommands.Save, Executed, CanExecute));

            var eventGroupTypeSet = new EventGroupTypeSet(ModelContainer.EventGroupTypeSet);
            Weeks = ModelContainer.EventGroupSet.Where(g => g.EventGroupType.Id == eventGroupTypeSet.Periodic.Id).ToList();
            ClassTypes = ModelContainer.EventGroupSet.Where(g => g.EventGroupType.Id == eventGroupTypeSet.Class.Id).ToList();
            ClassTypes.Sort((x, y) => StaticObjects.DefaultStringComparer.Compare(x.Name, y.Name));
            OrganizePersons = ModelContainer.PersonSet.ToList();
            OrganizePersons.Sort((x, y) => StaticObjects.DefaultStringComparer.Compare(x.FirstName, y.FirstName));

            SelectedWeeks = new List<EventGroup>();
            PersonGroupTypeSet = new PersonGroupTypeSet(modelContainer.PersonGroupTypeSet);
            EventGroupTypeSet = new EventGroupTypeSet(modelContainer.EventGroupTypeSet);

            var roleSet = new RoleSet(modelContainer.RoleSet);
            if (MainWindow.CurrentUser.Role.Name == roleSet.User.Name)
            {
                SelectedOrganizePerson = MainWindow.CurrentUser.Person;
                if (SelectedOrganizePerson == null)
                {
                    MessageBox.Show("Ваш логин не привязан к конкретному человеку. " + Environment.NewLine +
                                    "Обратитесь к администратору.");
                }
            }

            try
            {
                PeriodEnd = Settings.Default.PeriodEnd;
                PeriodStart = Settings.Default.PeriodStart;
            }
            catch (Exception)
            {
                PeriodStart = PeriodEnd = DateTime.Now;
            }

            SingleDate = DateTime.Now.Date;
            Pairs = PlaceTimetableViewModel.EventsByType;
            StartTime = start;
            EndTime = end;
            IsSingle = Settings.Default.IsSingle;
            CrossedEvents = new ObservableCollection<Event>();

            PropertyChanged += OnPropertyChanged;
            var firstPair = Pairs.FirstOrDefault(p => start <= p.StartTime.TimeOfDay && p.EndTime.TimeOfDay <= end);
            if (firstPair != null) SelectedPair = firstPair;
        }

        #endregion

        #region Properties

        public bool IsSingle
        {
            get { return Get(() => IsSingle); }
            set { Set(() => IsSingle, value); }
        }

        public string Name
        {
            get { return Get(() => Name); }
            set { Set(() => Name, value); }
        }

        public string Description
        {
            get { return Get(() => Description); }
            set { Set(() => Description, value); }
        }

        public Event SelectedPair
        {
            get { return Get(() => SelectedPair); }
            set { Set(() => SelectedPair, value); }
        }

        public DateTime SingleDate
        {
            get { return Get(() => SingleDate); }
            set { Set(() => SingleDate, value); }
        }

        public Place Place
        {
            get { return Get(() => Place); }
            set { Set(() => Place, value); }
        }

        public TimeSpan StartTime
        {
            get { return Get(() => StartTime); }
            set { Set(() => StartTime, value); }
        }

        public TimeSpan EndTime
        {
            get { return Get(() => EndTime); }
            set { Set(() => EndTime, value); }
        }

        public DateTime PeriodStart
        {
            get { return Get(() => PeriodStart); }
            set { Set(() => PeriodStart, value); }
        }

        public DateTime PeriodEnd
        {
            get { return Get(() => PeriodEnd); }
            set { Set(() => PeriodEnd, value); }
        }

        public EventGroup SelectedClassType
        {
            get { return Get(() => SelectedClassType); }
            set { Set(() => SelectedClassType, value); }
        }

        public Person SelectedOrganizePerson
        {
            get { return Get(() => SelectedOrganizePerson); }
            set { Set(() => SelectedOrganizePerson, value); }
        }

        public ModelContainer ModelContainer { get; set; }

        public List<Event> Pairs { get; set; }

        public ObservableCollection<Event> CrossedEvents { get; set; }

        public List<EventGroup> ClassTypes { get; set; }

        public List<EventGroup> Weeks { get; set; }

        public List<Person> OrganizePersons { get; set; }

        public List<EventGroup> SelectedWeeks { get; set; }

        private PersonGroupTypeSet PersonGroupTypeSet { get; set; }

        private EventGroupTypeSet EventGroupTypeSet { get; set; }

        private bool CanSave { get; set; }

        #endregion

        #region Methods

        private static IEnumerable<Event> CreateCrossedEventsQery(IQueryable<Event> events, Place place, DateTime from, DateTime to)
        {
            return
                events.
                    Where(e => e.Place.Id == place.Id &&
                               ((e.StartTime <= from && from < e.EndTime) ||
                                (e.StartTime < to && to <= e.EndTime) ||
                                (from <= e.StartTime && e.StartTime < to) ||
                                (from < e.EndTime && e.EndTime <= to)));
        }

        private void OnPropertyChanged(object sender, PropertyChangedEventArgs args)
        {
            if (args.PropertyName == PropertyNameProvider.GetPropertyName(() => SelectedPair))
            {
                StartTime = TimeSpan.Zero;
                EndTime = SelectedPair.EndTime.TimeOfDay;
                StartTime = SelectedPair.StartTime.TimeOfDay;            
            }

            if (args.PropertyName == PropertyNameProvider.GetPropertyName(() => StartTime))
            {
                if (StartTime > EndTime) EndTime = StartTime;
            }

            if (args.PropertyName == PropertyNameProvider.GetPropertyName(() => EndTime))
            {
                if (EndTime < StartTime) StartTime = EndTime;
            }

            if (args.PropertyName == PropertyNameProvider.GetPropertyName(() => PeriodStart))
            {
                if (PeriodStart > PeriodEnd) PeriodEnd = PeriodStart;
                Settings.Default.PeriodStart = PeriodStart;
            }

            if (args.PropertyName == PropertyNameProvider.GetPropertyName(() => PeriodEnd))
            {
                if (PeriodEnd < PeriodStart) PeriodStart = PeriodEnd;
                Settings.Default.PeriodEnd = PeriodEnd;
            }

            if (args.PropertyName == PropertyNameProvider.GetPropertyName(() => SelectedClassType))
            {
                Name = SelectedClassType.Name;
            }


            if (args.PropertyName == PropertyNameProvider.GetPropertyName(() => IsSingle))
            {
                Settings.Default.IsSingle = IsSingle;
            }

            CanSave = Validate() && Save();
        }

        private void CanExecute(object sender, CanExecuteRoutedEventArgs canExecuteRoutedEventArgs)
        {
            canExecuteRoutedEventArgs.CanExecute = CanSave;
        }

        private void Executed(object sender, ExecutedRoutedEventArgs executedRoutedEventArgs)
        {
            if (Validate())
                Save(true);
        }

        private bool Save(bool doSave = false)
        {
            var result = false;
            var selectedWeeks = SelectedWeeks.Any() ? SelectedWeeks : Weeks;
            var eventPeriodGroup = new EventGroup
                {
                    Name =
                        string.Format("Period: {0}-{1}",
                                      PeriodStart.ToShortDateString(),
                                      PeriodEnd.ToShortDateString()),
                    EventGroupType = EventGroupTypeSet.GroupByPeriod,
                };

            var start = PeriodStart;
            var end = PeriodEnd;

            if (IsSingle)
            {
                start = SingleDate;
                end = SingleDate;
            }
            else
            {
                while (start.DayOfWeek != SingleDate.DayOfWeek)
                    start = start.AddDays(1);
            }

            for (var date = start; date <= end; date = date.AddDays(7))
            {
                DateTime yearStart;
                var firstWeek = ConfigurationManager.AppSettings["FirstWeek"];
                if (!DateTime.TryParse(firstWeek, out yearStart))
                {
                    yearStart = new DateTime(date.Year, 1, 1);
                }

                var dayOfYear = (date - yearStart).Days;
                var weekNumber = dayOfYear/7;
                var week = weekNumber%4 + 1;

                if (!IsSingle && selectedWeeks.All(g => g.Name != week + "-я неделя"))
                    continue;
                
                var from = date + StartTime;
                var to = date + EndTime;
                var quary = CreateCrossedEventsQery(ModelContainer.EventSet, Place, from, to);
                CrossedEvents.Clear();
                CrossedEvents.AddRange(quary);
                var isBusy = CrossedEvents.Any();
                if (isBusy)
                    continue;

                if (!doSave)
                {
                    result = true;
                    continue;
                }

                var ev = ModelContainer.EventSet.CreateObject();
                ev.Place = Place;
                ev.EventGroup.Add(SelectedClassType);
                if (!IsSingle)
                    ev.EventGroup.Add(eventPeriodGroup);

                ev.Name = Name;
                ev.StartTime = from;
                ev.EndTime = to;
                ev.OrganizePersonGroup =
                    ModelContainer.PersonGroupSet.FirstOrDefault(
                        g =>
                        g.Person.Any(p => p.Id == SelectedOrganizePerson.Id) &&
                        g.PersonGroupType.Id == PersonGroupTypeSet.SinglePerson.Id);
                ev.EventGroup.AddRange(SelectedWeeks);
            }

            ModelContainer.HasChanges();
            ModelContainer.SaveChanges();
            return result;
        }

        private bool Validate()
        {
            return SelectedOrganizePerson != null && SelectedClassType != null;
        }

        #endregion
    }
}
